{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "9c794bc7",
   "metadata": {},
   "source": [
    "# 构建检索问答链"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3d0f2c3-3bd9-4de1-bbd2-e0e2b09161c8",
   "metadata": {},
   "source": [
    "我们已经介绍了如何根据自己的本地知识文档，搭建一个向量知识库。 在接下来的内容里，我们将使用搭建好的向量数据库，对 query 查询问题进行召回，并将召回结果和 query 结合起来构建 prompt，输入到大模型中进行问答。   "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95d8d968-8d98-47b9-8885-dc17d24dce76",
   "metadata": {},
   "source": [
    "## 1. 加载向量数据库Milvus\n",
    "\n",
    "首先，我们加载在前一章已经构建的向量数据库。注意，此处你需要使用和构建时相同的 Emedding。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "d8349782-7ca4-4ebb-acc1-6097ff5cee99",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2025-09-24 12:30:20,655 [DEBUG][_create_connection]: Created new connection using: async-http://localhost:19530 (async_milvus_client.py:599)\n"
     ]
    }
   ],
   "source": [
    "from langchain_community.vectorstores import Milvus\n",
    "from langchain_community.embeddings import OllamaEmbeddings\n",
    "from langchain_milvus import Milvus, BM25BuiltInFunction\n",
    "my_emb = OllamaEmbeddings(base_url='http://129.201.70.35:11434', model=\"dengcao/Qwen3-Embedding-0.6B:F16\")\n",
    "\n",
    "connection_args={\n",
    "            \"host\": \"129.201.70.35\",  # Milvus 服务器地址\n",
    "            \"port\": \"19530\",  # Milvus 默认端口\n",
    "        }\n",
    "\n",
    "vectordb = Milvus(\n",
    "    embedding_function=my_emb,\n",
    "    collection_name=\"ZXVMAXS6\",\n",
    "    vector_field=[\"dense\", \"sparse\"],  # dense 列 + sparse 列\n",
    "    connection_args=connection_args,\n",
    "    consistency_level=\"Strong\",\n",
    "    builtin_function=BM25BuiltInFunction()  # 启用 BM25\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "14c39416-a9b2-4de1-9fb6-5c521a7fd2f4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Document(metadata={'page': 13, 'subject': '', 'format': 'PDF 1.4', 'moddate': '', 'creator': 'DITA Open Toolkit', 'creationdate': '2023-05-23T21:45:33+08:00', 'title': '目录', 'producer': 'Apache FOP Version 2.6', 'trapped': '', 'file_path': '../data_base/knowledge_path/VMAX-S/ZXVMAX-S（V6.23）产品描述（上网日志业务）.pdf', 'pk': 461017040453078129, 'creationDate': \"D:20230523214533+08'00'\", 'source': '../data_base/knowledge_path/VMAX-S/ZXVMAX-S（V6.23）产品描述（上网日志业务）.pdf', 'keywords': '', 'total_pages': 29, 'author': '', 'modDate': ''}, page_content='4\\xa0功能本章包含如下主题：\\uf06c上网日志保存\\n10\\n\\uf06c上网日志查询\\n10\\n\\uf06c上网日志批量导入查询\\n11\\n\\uf06c日志管理\\n11\\n\\uf06c账号管理\\n11\\n\\uf06c角色管理\\n12\\n\\uf06c资源监控\\n12\\n\\uf06c告警管理\\n12\\n\\uf06c省级网关对接\\n12\\n\\uf06c拨测结果自动比对功能\\n12\\n\\uf06cNAT日志入库功能\\n13\\n\\uf06c北向接口\\n13\\n\\uf06c上网日志历史查询\\n13\\n\\uf06c云化上网日志XDR查询\\n13\\n以下介绍ZXVMAX-S的主要的功能。\\n4.1\\xa0上网日志保存\\uf06c支持使用Gbase数据库或HDFS保存上网日志。\\uf06c上网日志保存时间可配置，最短保存7天时间，最长可保存一年时间。\\uf06c支持自动清理超过保存时间的上网日志。\\n4.2\\xa0上网日志查询可通过web界面指定查询条件，查询用户上网日志。支持的查询条件：\\uf06c时间范围+公网IP\\n\\uf06c时间范围+目的IP\\n\\uf06c时间范围+MSISDN\\n\\uf06c时间范围+IMSI\\n\\uf06c时间范围+URL\\n\\uf06c支持组合以上五种基础条件，或在基础条件上增加指定公网端口、目的端口，做更精确的查询。\\n10\\nSJ-20220623151803-017|2023-03-30（R1.0）'),\n",
       " Document(metadata={'page': 297, 'subject': '', 'format': 'PDF 1.4', 'moddate': '', 'creator': 'DITA Open Toolkit', 'creationdate': '2022-06-23T16:34:22+08:00', 'title': '目录', 'producer': 'Apache FOP Version 2.3', 'trapped': '', 'file_path': '../data_base/knowledge_path/VMAX-S/ZXVMAX-S（V6.20.80.02）告警处理.pdf', 'pk': 461017040453077828, 'creationDate': \"D:20220623163422+08'00'\", 'source': '../data_base/knowledge_path/VMAX-S/ZXVMAX-S（V6.20.80.02）告警处理.pdf', 'keywords': '', 'total_pages': 330, 'author': '', 'modDate': ''}, page_content='ZXVMAX-S多维价值分析系统告警处理可能原因确保实际填充率达到预期填充率处理步骤确保实际填充率达到预期填充率\\n7.2194000200303HTTP接口下行TCP重传报文数完整率(小时)告警描述确保实际填充率达到预期填充率告警级别警告可能原因确保实际填充率达到预期填充率处理步骤确保实际填充率达到预期填充率\\n7.2204000200304HTTP接口TCP建链响应时延（ms）完整率(小时)告警描述确保实际填充率达到预期填充率告警级别警告可能原因确保实际填充率达到预期填充率处理步骤确保实际填充率达到预期填充率\\n7.2214000200305HTTP接口TCP建链确认时延（ms）完整率(小时)告警描述确保实际填充率达到预期填充率\\n276\\nSJ-20220623151803-011|2022-06-20（R1.0）')]"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "results = vectordb.similarity_search(query=\"什么是vmax的上网日志系统？\", k=2)\n",
    "results"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f7f8dbd-ecd5-449d-9753-aedc2b74289c",
   "metadata": {},
   "source": [
    "## 2. 创建一个 LLM"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "026bd74f-3dd0-496e-905b-950a444bb7a7",
   "metadata": {},
   "source": [
    "在这里，我们调用 OpenAI 的 API 创建一个 LLM，当然你也可以使用其他 LLM 的 API 进行创建"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "a89fb297-888a-4d35-b519-90eba639893c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_community.llms import Ollama\n",
    "\n",
    "my_llm = Ollama(base_url='http://129.201.70.35:11434', model='qwen3:8B', temperature=0.1)\n",
    "\n",
    "# my_llm.invoke(\"你好\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f361e27-cafb-48bf-bb41-50c9cb3a4f7e",
   "metadata": {},
   "source": [
    "## 3. 构建检索问答链"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "248b5e3c-1bc9-40e9-83c7-0594c2e7727d",
   "metadata": {},
   "source": [
    "prompts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "91be03f4-264d-45cb-bebd-223c1c5747fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts import PromptTemplate\n",
    "\n",
    "template = \"\"\"你是VMAX运维助手，使用以下上下文来回答问题。如果你不知道答案，就说你不知道，不要试图编造答案。总是在回答的最后说“谢谢你的提问！”。\n",
    "{context}\n",
    "问题: {question}\n",
    "\"\"\"\n",
    "\n",
    "QA_CHAIN_PROMPT = PromptTemplate(input_variables=[\"context\",\"question\"],\n",
    "                                 template=template)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2d06d7f-1dca-4d10-b5cd-3a23e9d91200",
   "metadata": {},
   "source": [
    "#### 创建一个基于模板的检索链： 基础检索版本"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "8b05eb57-edf5-4b35-9538-42c2b8f5cc16",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chains import RetrievalQA\n",
    "\n",
    "# 基础检索\n",
    "base_retriever = vectordb.as_retriever(search_kwargs={\"k\": 10})\n",
    "base_retriever = vectordb.as_retriever(\n",
    "    search_kwargs={\"k\": 15},  # 扩大召回池\n",
    "    search_type=\"mmr\",  # 最大边际相关性算法（网页5）\n",
    "    metadata_filter={\"source\": \"../data_base/knowledge_path/VMAX-S/ZXVMAX-S（V6.23）产品描述（5GC业务）.pdf\"}  # 元数据过滤\n",
    ")\n",
    "\n",
    "qa_chain = RetrievalQA.from_chain_type(my_llm,\n",
    "                                       retriever=base_retriever,\n",
    "                                       return_source_documents=True,\n",
    "                                       chain_type_kwargs={\"prompt\":QA_CHAIN_PROMPT})\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7cea560-86e0-491e-b4b8-d0239c9a51df",
   "metadata": {},
   "source": [
    "#### 创建一个基于模板的检索链： 混合检索版本"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b141c333-bb07-4054-be61-4c7312d3c7bf",
   "metadata": {},
   "source": [
    "混合检索"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f013783-8e04-47e3-b04a-62ad366b71bd",
   "metadata": {},
   "source": [
    "Milvus自带混合检索功能，\n",
    "数据写入向量数据库流程需要修改\n",
    "\n",
    "参考： https://milvus.io/docs/zh/milvus_hybrid_search_retriever.md\n",
    "\n",
    "修改147-156行： "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "d232ac41-5f07-4802-ba0c-eb0cb0040790",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 如果使用Milvus的混合检索\n",
    "            # vectordb = Milvus.from_documents(\n",
    "            # documents=batch_docs,\n",
    "            # embedding=my_emb,\n",
    "            # builtin_function=BM25BuiltInFunction(),\n",
    "            # vector_field=[\"dense\", \"sparse\"],\n",
    "            # collection_name=\"ZXVMAXS5\",\n",
    "            # drop_old=False,\n",
    "            # connection_args=connection_args,\n",
    "            # consistency_level=\"Strong\",\n",
    "            # )       "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "aac2fba2-3e6b-4668-a0f3-669253a7ab33",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2025-09-24 12:30:51,323 [DEBUG][_create_connection]: Created new connection using: async-http://localhost:19530 (async_milvus_client.py:599)\n"
     ]
    }
   ],
   "source": [
    "from langchain_milvus import Milvus, BM25BuiltInFunction\n",
    "from langchain_community.embeddings import OllamaEmbeddings\n",
    "my_emb = OllamaEmbeddings(base_url='http://129.201.70.35:11434', model=\"dengcao/Qwen3-Embedding-0.6B:F16\")\n",
    "\n",
    "# Milvus 连接参数\n",
    "vectordb = Milvus(\n",
    "        embedding_function=my_emb,\n",
    "        collection_name=\"ZXVMAXS6\",  # Milvus 集合名称\n",
    "        connection_args={\n",
    "            \"host\": \"129.201.70.35\",  # Milvus 服务器地址\n",
    "            \"port\": \"19530\",  # Milvus 默认端口\n",
    "        },\n",
    "        builtin_function=BM25BuiltInFunction(),\n",
    "        vector_field=[\"dense\", \"sparse\"]\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25f9c5e9-3728-41ba-ab4c-d665b4669fc8",
   "metadata": {},
   "source": [
    "定义检索器，从向量数据库检索与问题相关的文档片段"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "7dcd1a60-a694-445e-8005-f0aa8b60a406",
   "metadata": {},
   "outputs": [],
   "source": [
    "retriever = vectordb.as_retriever()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40b85408-4889-4df9-bb28-be28be00e058",
   "metadata": {},
   "source": [
    "将检索到的多个文档合并为单一字符串，作为上下文的输入"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "b108d78b-0601-4232-a501-e7ad510a2aee",
   "metadata": {},
   "outputs": [],
   "source": [
    "def format_docs(docs):\n",
    "    return \"\\n\\n\".join(doc.page_content for doc in docs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "caf48c84-ca0a-464e-be03-1b3b9b1ced7b",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "42049855-c206-423c-a6f1-0fce4bd7d0d7",
   "metadata": {},
   "source": [
    "​​输入处理​​：  \n",
    "RunnablePassthrough()直接传递用户问题到question字段。  \n",
    "retriever | format_docs：检索文档并格式化为字符串，作为context。  \n",
    "​​提示生成​​：prompt将context和question填充到模板中。  \n",
    "​​模型调用​​：llm生成回答。  \n",
    "​​输出解析​​：StrOutputParser将模型输出转为字符串。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "b9ed2b88-aa66-491e-a7cf-90a86d293d6a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.runnables import RunnablePassthrough\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "\n",
    "rag_chain = (\n",
    "    {\"context\": retriever | format_docs, \"question\": RunnablePassthrough()}\n",
    "    | QA_CHAIN_PROMPT \n",
    "    | my_llm\n",
    "    | StrOutputParser()\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "3bb64155-b6f3-48a4-9b9c-ced92052100b",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'<think>\\n好的，用户问的是ZXVMAX-S上网日志有哪些功能。首先，我需要仔细查看提供的上下文内容，看看有没有直接提到上网日志相关的信息。\\n\\n首先，在产品定位与特点部分，提到了ZXVMAX-S端到端系统有用户分析、网络分析、专题分析和评估优化四大功能。还提到支持XDR查询、实时保障、多维分析、会话跟踪等功能。这里可能和上网日志有关，但需要更具体的信息。\\n\\n接下来，在产品特点部分，分层架构、可靠性设计等，但没有直接提到上网日志。然后，在可靠性设计中的软件可靠性部分，提到了容错能力、故障检测处理，但依然没有上网日志的内容。\\n\\n再仔细看产品描述的数据业务部分，提到ZXVMAX-S支持XDR查询、实时保障、多维分析、会话跟踪。这些功能可能与上网日志相关，比如XDR查询可能涉及用户数据记录，会话跟踪可能涉及上网行为的监控。但上下文中没有明确提到“上网日志”这个具体功能名称。\\n\\n用户的问题是关于上网日志的功能，而提供的上下文没有直接提到上网日志的具体功能点。因此，根据指示，如果不知道答案，应该直接说不知道，不编造。所以，正确的回答应该是告知用户当前上下文没有相关信息，因此无法提供答案，并在最后加上谢谢。\\n</think>\\n\\n根据提供的上下文内容，没有明确提到ZXVMAX-S上网日志的具体功能。因此，我无法提供相关答案。谢谢你的提问！'"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "query = \"ZXVMAX-S上网日志有哪些功能？\"\n",
    "res = rag_chain.invoke(query)\n",
    "res\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bcf84b54-e103-45dc-99c8-6a40533e0b08",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "7d7d8ac0-b82d-4df5-81e0-588c076cb268",
   "metadata": {},
   "source": [
    "#### 加入ReRanker进行精排"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00d6cfc0-05e4-4614-85f9-3ad24d6dcbcf",
   "metadata": {},
   "source": [
    "第一阶段检索结束后，我们需要对候选数据重新排名，以获得更好的结果。您可以根据自己的要求选择加权排名器（WeightedRanker）或重新 排名 器（RRFRanker）。\n",
    "\n",
    "WeightedRanker/RRF：是一种多路召回融合算法，通过加权融合不同检索路径（如向量搜索、关键词搜索）的排名结果，提升召回多样性。但它仅基于排名位置计算分数，不深入分析语义相关性。\n",
    "\n",
    "Cohere等 Rerank：是基于深度学习的交叉编码器（Cross-Encoder），直接计算查询与文档的语义匹配分数，能更精准地识别上下文关联性，尤其适合处理复杂语义或多义词问题。\n",
    "\n",
    "所以可以进一步进行Reranker"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8fd84415-5fcd-4bf8-9d2d-ab4e3324c3fd",
   "metadata": {},
   "source": [
    "构建RAG"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "d843f790-8843-473b-a196-8959c28563dd",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2025-09-24 12:32:11,201 [DEBUG][_create_connection]: Created new connection using: async-http://localhost:19530 (async_milvus_client.py:599)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'<think>\\n好的，用户问的是ZXVMAX-S上网日志有哪些功能。我需要先仔细看看提供的上下文内容，找到相关部分。\\n\\n首先，在4.1节提到上网日志保存，支持Gbase或HDFS保存，保存时间可配置，7天到一年，并且自动清理过期日志。这部分是关于日志存储的。\\n\\n然后4.2节是上网日志查询，通过web界面，支持多种查询条件，比如时间范围加公网IP、目的IP、MSISDN、IMSI、URL，还可以组合这些条件，或者加上端口进行更精确的查询。这里详细列出了查询条件和组合方式。\\n\\n接下来还有其他功能，比如4.3可能提到批量导入查询，但用户的问题可能只关注日志保存和查询。不过需要确认是否有其他相关部分。比如后面提到的NAT日志入库、北向接口、历史查询、XDR查询等，这些可能属于上网日志相关的功能，但需要看是否在上下文中明确提到。\\n\\n另外，产品定位和特点里提到支持XDR查询、实时保障、多维分析等功能，但不确定是否直接属于上网日志的功能。需要区分哪些是日志管理相关的，哪些是其他分析功能。\\n\\n用户的问题明确是关于上网日志的功能，所以应该集中在4.1和4.2节，以及可能的其他相关部分。比如4.3可能提到批量导入查询，但原文中4.3可能没有详细内容，因为用户提供的上下文里4.3可能被省略了。需要检查用户提供的上下文是否有其他部分。\\n\\n在用户提供的上下文中，4.1和4.2是明确的，而其他如日志管理、账号管理等可能属于不同的功能模块，但可能和日志相关。不过问题只问上网日志的功能，所以应该只回答保存和查询相关的内容，以及可能的其他日志相关功能如批量导入查询、历史查询、XDR查询等，如果上下文中有提到的话。\\n\\n例如，在4.3可能提到上网日志批量导入查询，但用户提供的上下文里可能没有详细展开，但根据用户给出的目录，4.3是上网日志批量导入查询，可能属于功能之一。同样，上网日志历史查询和云化XDR查询可能也是相关功能。\\n\\n因此，综合来看，ZXVMAX-S上网日志的功能包括保存、查询、批量导入查询、历史查询、XDR查询，以及可能的NAT日志入库和北向接口等。但需要根据上下文中的具体描述来确认。\\n\\n在用户提供的上下文中，4.1和4.2是明确的，而其他如4.3可能提到批量导入查询，4.4日志管理可能涉及日志的管理功能，但不确定是否属于上网日志的直接功能。需要仔细检查用户提供的上下文内容。\\n\\n根据用户提供的上下文，4.1到4.3可能包括：\\n\\n- 上网日志保存（4.1）\\n- 上网日志查询（4.2）\\n- 上网日志批量导入查询（4.3）\\n- 日志管理（4.4）\\n- 账号管理（4.5）\\n- 角色管理（4.6）\\n- 资源监控（4.7）\\n- 告警管理（4.8）\\n- 省级网关对接（4.9）\\n- 拨测结果自动比对（4.10）\\n- NAT日志入库（4.11）\\n- 北向接口（4.12）\\n- 上网日志历史查询（4.13）\\n- 云化XDR查询（4.14）\\n\\n所以，上网日志相关的功能包括4.1、4.2、4.3、4.13、4.14，以及可能的NAT日志入库（4.11）和北向接口（4.12）是否属于上网日志功能。需要根据上下文中的描述来判断。\\n\\n例如，4.11 NAT日志入库功能可能属于上网日志的一部分，而北向接口可能用于数据传输，但可能不直接属于日志功能。因此，回答时应包括保存、查询、批量导入查询、历史查询、XDR查询，以及NAT日志入库，如果上下文中有提到的话。\\n\\n总结，用户的问题需要列出所有与上网日志相关的功能，根据上下文中的目录和描述，应该包括：\\n\\n1. 上网日志保存（支持Gbase/HDFS，时间配置，自动清理）\\n2. 上网日志查询（多种条件，组合查询）\\n3. 上网日志批量导入查询\\n4. 上网日志历史查询\\n5. 云化上网日志XDR查询\\n6. NAT日志入库功能\\n7. 北向接口（可能用于日志传输）\\n\\n但需要确认是否这些都属于上网日志的功能。例如，NAT日志入库可能属于日志保存的一部分，而XDR查询是查询功能。因此，最终回答应涵盖这些点，但根据用户提供的上下文，可能需要更准确地引用内容。\\n</think>\\n\\nZXVMAX-S上网日志的功能包括：  \\n1. **上网日志保存**：支持使用Gbase数据库或HDFS保存日志，保存时间可配置（7天至1年），并支持自动清理过期日志。  \\n2. **上网日志查询**：通过Web界面，支持按时间范围+公网IP、目的IP、MSISDN、IMSI、URL等条件查询，且可组合基础条件或增加端口等参数进行精确查询。  \\n3. **上网日志批量导入查询**：支持批量导入日志数据并进行查询。  \\n4. **上网日志历史查询**：提供历史日志的查询功能。  \\n5. **云化上网日志XDR查询**：支持云化环境下的XDR（可扩展数据记录）日志查询。  \\n6. **NAT日志入库功能**：支持将NAT日志入库存储。  \\n7. **北向接口**：提供北向接口用于数据交互。  \\n\\n谢谢你的提问！'"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.retrievers import ContextualCompressionRetriever\n",
    "from langchain.retrievers.document_compressors import CrossEncoderReranker\n",
    "from langchain_community.cross_encoders import HuggingFaceCrossEncoder\n",
    "from langchain_community.llms import Ollama\n",
    "from langchain_community.vectorstores import Milvus\n",
    "from langchain_community.embeddings import OllamaEmbeddings\n",
    "from langchain_core.runnables import RunnablePassthrough\n",
    "from langchain_core.prompts import PromptTemplate\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain.prompts import PromptTemplate\n",
    "\n",
    "\n",
    "from langchain_milvus import Milvus, BM25BuiltInFunction\n",
    "from langchain_community.embeddings import OllamaEmbeddings\n",
    "my_emb = OllamaEmbeddings(base_url='http://129.201.70.35:11434', model=\"dengcao/Qwen3-Embedding-0.6B:F16\")\n",
    "\n",
    "# Milvus 连接参数\n",
    "vectordb = Milvus(\n",
    "        embedding_function=my_emb,\n",
    "        collection_name=\"ZXVMAXS6\",  # Milvus 集合名称\n",
    "        connection_args={\n",
    "            \"host\": \"129.201.70.35\",  # Milvus 服务器地址\n",
    "            \"port\": \"19530\",  # Milvus 默认端口\n",
    "        },\n",
    "        builtin_function=BM25BuiltInFunction(),\n",
    "        vector_field=[\"dense\", \"sparse\"]\n",
    "    )\n",
    "\n",
    "my_llm = Ollama(base_url='http://129.201.70.35:11434', model='qwen3:8B', temperature=0.1)\n",
    "\n",
    "\n",
    "template = \"\"\"你是VMAX运维助手，使用以下上下文来回答问题。如果你不知道答案，就说你不知道，不要试图编造答案。总是在回答的最后说“谢谢你的提问！”。\n",
    "{context}\n",
    "问题: {question}\n",
    "\"\"\"\n",
    "\n",
    "QA_CHAIN_PROMPT = PromptTemplate(input_variables=[\"context\",\"question\"],\n",
    "                                 template=template)\n",
    "\n",
    "\n",
    "# 初始化重排模型（显式指定设备）\n",
    "rerank_model = HuggingFaceCrossEncoder(\n",
    "    # model_name=\"/opt/workspace/models/Qwen/Qwen3-Reranker-0.6B\",\n",
    "    model_name=\"/opt/workspace/models/BAAI/bge-reranker-base\",\n",
    ")\n",
    "\n",
    "# 配置重排器（调整 top_n 和批处理大小）\n",
    "base_compressor = CrossEncoderReranker(\n",
    "    model=rerank_model,\n",
    "    top_n=5,  # 根据业务需求调整\n",
    "    # batch_size=8  # 加速批量查询\n",
    "    \n",
    ")\n",
    "\n",
    "# 创建压缩检索器（结合 Milvus 和 Rerank）\n",
    "compression_retriever = ContextualCompressionRetriever(\n",
    "    base_compressor=base_compressor,\n",
    "    base_retriever=vectordb.as_retriever(search_kwargs={\"k\": 15})  # 初步召回 15 条\n",
    ")\n",
    "\n",
    "\n",
    "def format_docs(docs):\n",
    "    return \"\\n\\n\".join(doc.page_content for doc in docs)\n",
    "\n",
    "\n",
    "\n",
    "rag_chain = (\n",
    "    {\"context\": compression_retriever | format_docs, \"question\": RunnablePassthrough()}\n",
    "    | QA_CHAIN_PROMPT \n",
    "    | my_llm\n",
    "    | StrOutputParser()\n",
    ")\n",
    "\n",
    "query = \"ZXVMAX-S上网日志有哪些功能？\"\n",
    "res = rag_chain.invoke(query)\n",
    "res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dd3c0daf-2eb2-4ca7-ac70-239ae74ab30f",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "env_rag",
   "language": "python",
   "name": "env_rag"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
